package cn.demoncat.util.io;

import cn.demoncat.util.exception.BizRuntimeException;
import cn.demoncat.util.exception.IoRuntimeException;
import cn.demoncat.util.io.constant.FileConstant.FileZoomTag;
import cn.demoncat.util.io.constant.ImgConstant;
import cn.demoncat.util.io.entity.ImgParam;
import cn.demoncat.util.lang.*;
import cn.demoncat.util.lang.constant.StringConstant;
import cn.demoncat.util.lang.msg.FileMsg;
import cn.demoncat.util.web.ServletUtil;
import cn.demoncat.util.web.UrlUtil;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.Thumbnails.Builder;
import net.coobird.thumbnailator.filters.Watermark;
import net.coobird.thumbnailator.geometry.Positions;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * 图片处理（Thumbnailator）
 * 
 * @author 延晓磊
 *
 * @since 2018年12月16日
 */
public final class ImgUtil {
 
	/**
	 * 水印图片缓存
	 */
	public static final Map<String,Watermark> WATERMARK_CACHE = new HashMap<>();
	
	/**
	 * 判断是否为图片
	 * 
	 * @param typeOrExt	内容类型/扩展名（不包含.）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年12月16日
	 */
	public static boolean check(String typeOrExt) {
		if (StringUtils.isBlank(typeOrExt)) {
			return false;
		}
		String type = typeOrExt.toLowerCase();
		if (type.contains(ImgConstant.MIME_PRE)) {
			return true;
		}else {
			return ArrayUtils.contains(ImgConstant.Format.ALL, typeOrExt);
		}
	}
	
	/**
	 * 获取图片格式（扩展名）
	 * 
	 * @param name
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月17日
	 */
	public static String getFormat(String name) {
		// 截取?号
		if (name.contains(StringConstant.QUESTION)) {
			name = name.substring(0, name.indexOf(StringConstant.QUESTION));
		}
		// 截取#号
		if (name.contains(StringConstant.HASH)) {
			name = name.substring(0, name.indexOf(StringConstant.HASH));
		}
		// 获取扩展名
		String ext = FileNameUtil.getExtension(name);
		if (StringUtils.isBlank(ext) || !ArrayUtils.contains(ImgConstant.Format.ALL, ext)) {
			return ImgConstant.Format.PNG;
		}else {
			return ext;
		}
	}

	/**
	 * 预估原图大小
	 *
	 * @param base64Size	BASE64编码大小
	 * @return  1000 > 750
	 *
	 * @author 延晓磊
	 * @since 2020年10月25日
	 */
	public static long predictImgSize(long base64Size) {
		// Base64编码，要求将3个8位字节（3*8=24）转化为4个6位的字节（4*6=24），然后在6位的前面补两个0，形成8位一个字节的形
		return base64Size - (base64Size/8) * 2;
	}

	/**
	 * 预估BASE64大小
	 *
	 * @param imgSize	原图大小
	 * @return  750 > 1000
	 *
	 * @author 延晓磊
	 * @since 2020年10月25日
	 */
 	public static long predictBase64Size(long imgSize) {
 		// 每6位为1字节，补2位0
		return imgSize + imgSize/6*2;
	}

	/**
	 * 获取图片输出流
	 * 
	 * 注意：WEB下载完成后须返回null或void，避免SpringMvc二次输出响应
	 * 
	 * @param response
	 * @param name		图片名称，扩展名默认png
	 * @param type		是否匹配MIME类型
	 * 
	 * @return out
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static OutputStream getOutput(HttpServletResponse response, String name, boolean type) {
		if (StringUtils.isBlank(name)) {
			name = LocalDateUtil.toDateStr();
		}
		// 无扩展名
		if (!name.contains(StringConstant.POINT)) {
			name = name + ImgConstant.Suffix.PNG;
		}
		return ServletUtil.getDownloadOut(response, name, type);
	}
	
	/**
	 * 获取BufferedImage
	 * 
	 * @param img	图片(流关闭)
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static BufferedImage getImg(File img) {
		try {
			return ImageIO.read(img);
		} catch (IOException e) {
			throw new IoRuntimeException(FileMsg.IMG_READ_ERROR, e);
		}
	}
	
	/**
	 * 获取BufferedImage
	 * 
	 * @param img	图片(流关闭)
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static BufferedImage getImg(InputStream img) {
		try {
			return ImageIO.read(img);
		} catch (IOException e) {
			throw new IoRuntimeException(FileMsg.IMG_READ_ERROR, e);
		}finally {
			CloseUtil.close(img);
		}
	}
	
	/**
	 * 获取BufferedImage
	 * 
	 * @param img	图片
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static BufferedImage getImg(byte[] img) {
		return getImg(new ByteArrayInputStream(img));
	}
	
	/**
	 * 获取BufferedImage
	 * 
	 * @param img	图片URL
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static BufferedImage getImg(String img) {
		try {
			return ImageIO.read(UrlUtil.getUrl(img));
		} catch (IOException e) {
			throw new IoRuntimeException(FileMsg.IMG_READ_ERROR, e);
		}
	}
	
	/**
	 * 获取（原生处理）缩放的BufferedImage
	 * 
	 * @param src		源图
	 * @param width		宽
	 * @param height	高
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static BufferedImage getImg(BufferedImage src, int width, int height) {
		// 创建新图片
		BufferedImage newImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB ); 
		// 绘制：x起点，y起点，宽，高
		newImg.getGraphics().drawImage(src, 0, 0, width, height, null);
		return newImg;
	}
	
	/**
	 * 获取（原生处理）缩放的BufferedImage
	 * 
	 * @param src		源图(流关闭)
	 * @param width		宽
	 * @param height	高
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static BufferedImage getImg(InputStream src, int width, int height) {
		return getImg(getImg(src), width, height);
	}
	
	/**
	 * 获取Thumbnails处理的BufferedImage
	 * 
	 * @param img		源图
	 * @param param		图片处理参数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static BufferedImage getImg(Builder<?> img, ImgParam param) {
		// 设置参数
		setParam(img, param);
		try {
			// 转换为BufferedImage
			return img.asBufferedImage();
		} catch (IOException e) {
			throw new IoRuntimeException(FileMsg.IMG_READ_ERROR, e);
		}
	}
	
	/**
	 * 获取Thumbnails处理的BufferedImage
	 * 
	 * @param img		源图
	 * @param param		图片处理参数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static BufferedImage getImg(BufferedImage img, ImgParam param) {
		return getImg(Thumbnails.of(img), param);
	}
	
	/**
	 * 获取Thumbnails处理的BufferedImage
	 * 
	 * @param img		源图(流关闭)
	 * @param param		图片处理参数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static BufferedImage getImg(InputStream img, ImgParam param) {
		return getImg(getImg(img), param);
	}
	
	/**
	 * 直接输出图片
	 * 
	 * @param img		图片
	 * @param out		输出流(png)，已关闭
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月9日
	 */
	public static void out(BufferedImage img, OutputStream out) {
		out(img, null, out);
	}
	
	/**
	 * 直接输出图片
	 * 
	 * @param img		图片
	 * @param format	格式：ImgConstant.Format
	 * @param out		输出流，已关闭
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月9日
	 */
	public static void out(BufferedImage img, String format, OutputStream out) {
		try {
			ImageIO.write(img, StringUtil.toDefault(format, ImgConstant.Format.PNG), out);
		} catch (IOException e) {
			throw new IoRuntimeException(FileMsg.IMG_OUT_ERROR, e);
		}finally {
			CloseUtil.close(out);
		}
	}
 
	/**
	 * 处理并输出图片
	 * 
	 * @param img		源图
	 * @param out		输出图片
	 * @param param		图片处理参数
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static void out(Builder<?> img, OutputStream out, ImgParam param) {
		// 设置参数
		setParam(img, param);
		// 输出图片
		try {
			img.toOutputStream(out);
		} catch (IOException e) {
			throw new IoRuntimeException(FileMsg.IMG_HANDLE_ERROR, e);
		}finally {
			if (param.isClose()) {
				CloseUtil.close(out);
			}
		}
	}
	
	/**
	 * 处理并输出图片
	 * 
	 * @param img		源图
	 * @param file		输出图片路径
	 * @param param		图片处理参数
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static void out(Builder<?> img, String file, ImgParam param) {
		out(img, IoUtil.getOutputStreamByFile(file), param);
	}

	/**
	 * 处理并输出图片
	 * 
	 * @param img		源图
	 * @param out		输出图片
	 * @param param		图片处理参数(需要指定format，默认png)
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static void out(BufferedImage img, OutputStream out, ImgParam param) {
		// BufferedImage需要指定format
		if (StringUtils.isBlank(param.getFormat())) {
			param.format(ImgConstant.Format.PNG);
		}
		out(Thumbnails.of(img), out, param);
	}
	
	/**
	 * 处理并输出图片
	 * 
	 * @param img		源图
	 * @param file		输出图片路径
	 * @param param		图片处理参数
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static void out(BufferedImage img, String file, ImgParam param) {
		// BufferedImage需要指定format
		if (StringUtils.isBlank(param.getFormat())) {
			param.format(ImgUtil.getFormat(file));
		}
		// 处理并输出
		out(Thumbnails.of(img), file, param);
	}
	
	/**
	 * 处理并输出图片
	 * 
	 * @param in		源图
	 * @param out		输出图片
	 * @param param		图片处理参数
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static void out(InputStream in, OutputStream out, ImgParam param) {
		// 读取图片
		Builder<? extends InputStream> img = Thumbnails.of(in);
		// 设置参数
		setParam(img, param);
		// 输出图片
		try {
			if (param.getAlpha() > 0) {
				// 转PNG
				ByteArrayOutputStream os = IoUtil.getOutputStream();
				img.toOutputStream(os);
				toPng(IoUtil.getInputStream(os.toByteArray()), out, param.getAlpha());
			}else{
				img.toOutputStream(out);
			}
		} catch (IOException e) {
			throw new IoRuntimeException(FileMsg.IMG_HANDLE_ERROR, e);
		}finally {
			if (param.isClose()) {
				CloseUtil.close(in);
				CloseUtil.close(out);
			}
		}
	}
	
	/**
	 * 处理并输出图片
	 * 
	 * @param img		源图
	 * @param out		输出图片
	 * @param param		图片处理参数
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static void out(byte[] img, OutputStream out, ImgParam param) {
		out(new ByteArrayInputStream(img), out, param);
	}
	
	/**
	 * 处理并输出图片
	 * 
	 * @param img		源图
	 * @param param		图片处理参数
	 * 
	 * @return 输出图片
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static byte[] out(InputStream img, ImgParam param) {
		// 数组流
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		// 处理并输出图片
		out(img, out, param);
		// 返回图片数组
		return out.toByteArray();
	}
	
	/**
	 * 处理并输出图片
	 * 
	 * @param img		源图
	 * @param param		图片处理参数
	 * 
	 * @return 输出图片
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static byte[] out(byte[] img, ImgParam param) {
		return out(new ByteArrayInputStream(img), param);
	}

	/**
	 * 输出文字图片
	 *
	 * @param text  	文字
	 * @param out       输出流
	 * @param bgColor   背景色，默认透明
	 * @param color     字色，默认黑色
	 * @param font      字体，例 new Font("宋体", Font.PLAIN, 16)
	 * @param width     宽
	 * @param height    高
	 * @param left      左间距，-1表示居中
	 * @param top       顶间距，-1表示居中
	 *
	 * @author 延晓磊
	 * @since 2023年10月23日
	 */
	public static void out(String text, OutputStream out, Color bgColor, Color color, Font font, int width, int height, int left, int top ){
		// 创建图片
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		// 创建图表
		Graphics2D graphics = image.createGraphics();
		// 设置背景色
		if (bgColor != null) {
			graphics.setColor(bgColor);
			graphics.fillRect(0, 0, width, height);
		}
		// 设置字色
		graphics.setColor(ObjectUtil.toDefault(color, Color.BLACK));
		// 设置字体
		graphics.setFont(font);
		// 绘制文字
		FontMetrics fontMetrics = graphics.getFontMetrics();
		if (left < 0) {
			// 左间距
			left = (width - fontMetrics.stringWidth(text)) / 2;
		}
		if (top < 0) {
			// 顶间距
			top = (height - fontMetrics.getHeight()) / 2 + fontMetrics.getAscent();
		}else{
			top += font.getSize();
		}
		graphics.drawString(text, left, top);
		// 保存图片
		try {
			ImageIO.write(image, ImgConstant.Format.PNG, out);
		} catch (IOException e) {
			throw new IoRuntimeException(FileMsg.IMG_OUT_ERROR, e);
		}finally {
			CloseUtil.close(out);
			graphics.dispose();
		}
	}

	/**
	 * 缩放图片
	 * 
	 * @param in		输入图片(流关闭)
	 * @param out		输出图片(流关闭)
	 * @param width		宽：如果为null则按高度等比例，如果宽高都不为空则强制拉伸
	 * @param height	高：如果为null则按宽度等比例，如果宽高都不为空则强制拉伸	
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static void zoom(InputStream in, OutputStream out, Integer width, Integer height) {
		out(in, out, ImgParam.build().width(width).height(height));
	}

	/**
	 * 缩放图片
	 *
	 * @param in		输入图片(流关闭)
	 * @param width		宽：如果为null则按高度等比例，如果宽高都不为空则强制拉伸
	 * @param height	高：如果为null则按宽度等比例，如果宽高都不为空则强制拉伸
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static byte[] zoom(InputStream in, Integer width, Integer height) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		zoom(in, out, width, height);
		return out.toByteArray();
	}

	/**
	 * 缩放图片
	 * 
	 * @param in	输入图片(流关闭)
	 * @param out	输出图片(流关闭)
	 * @param zoom	缩放标识：100 表示width=100(对称拉伸)；100o100表示width=100|height=100(智能拉伸)；100x100表示width=100&height=100(强制拉伸)		
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static void zoom(InputStream in, OutputStream out, String zoom) {
		out(in, out, ImgParam.build().zoom(zoom));
	}

	/**
	 * 缩放图片
	 *
	 * @param in	输入图片(流关闭)
	 * @param zoom	缩放标识：100 表示width=100(对称拉伸)；100o100表示width=100|height=100(智能拉伸)；100x100表示width=100&height=100(强制拉伸)
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static byte[] zoom(InputStream in, String zoom) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		zoom(in, out, zoom);
		return out.toByteArray();
	}
	
	/**
	 * 压缩图片（根据限额压缩）
	 * 
	 * @param in	输入图片(流关闭)
	 * @param out	输出图片(流关闭)
	 * @param limit	限额（压缩阀值）
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static void zoom(InputStream in, OutputStream out, long limit) {
		long size;
		try {
			size = in.available();
		} catch (IOException e) {
			throw new IoRuntimeException(FileMsg.IMG_READ_ERROR, e);
		}
		out(in, out, ImgParam.build().quality(size, limit));
	}

	/**
	 * 压缩图片（根据限额压缩）
	 *
	 * 注：部分图片会压缩失败，比如.bmp
	 *
	 * @param in	输入图片(流关闭)
	 * @param limit	限额（压缩阀值）
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static byte[] zoom(InputStream in,long limit) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		zoom(in, out, limit);
		if (out.size() > limit) {
			// 可以对jpg压缩，但对bmp会失效
			throw new BizRuntimeException("图片压缩失败，请变更格式后再试");
		}
		return out.toByteArray();
	}

	/**
	 * 压缩图片（根据比例压缩）
	 * 
	 * @param in	输入图片(流关闭)
	 * @param out	输出图片(流关闭)
	 * @param quality	比例
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static void zoom(InputStream in, OutputStream out, Double quality) {
		out(in, out, ImgParam.build().quality(quality));
	}

	/**
	 * 压缩图片（根据比例压缩）
	 *
	 * @param in	输入图片(流关闭)
	 * @param quality	比例
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static byte[] zoom(InputStream in, Double quality) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		zoom(in, out, quality);
		return out.toByteArray();
	}
	
	/**
	 * 缩放图片（综合尺寸缩放和质量压缩）
	 * <pre>
	 * 缩放标识说明：
	 * 100 		width=100(对称拉伸)
	 * 100o100	width=100|height=100(智能拉伸)
	 * 100x100	width=100&height=100(强制拉伸)
	 * L10000	尺寸大于10000时，以10000为限额计算比例并压缩
	 * Q0.1		以0.1的比例压缩
	 * </pre>
	 * @param in	输入图片(流关闭)
	 * @param out	输出图片(流关闭)
	 * @param zoom	缩放标识：FileZoomTag	
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static void zooms(InputStream in, OutputStream out, String zoom) {
		if (StringUtils.isNotBlank(zoom)) {
			// 前缀标记
			String tag = StringUtil.get(zoom, 0);
			if (FileZoomTag.QUALITY_LIMIT.equals(tag)) {
				// 限额压缩
				zoom(in, out, LongUtil.valueOf(zoom.substring(1)));
			}else if (FileZoomTag.QUALITY.equals(tag)) {
				// 比例压缩
				zoom(in, out, DoubleUtil.valueOf(zoom.substring(1)));
			}else {
				// 尺寸缩放
				zoom(in, out, zoom);
			}
		}else {
			out(in, out, ImgParam.build());
		}
	}

	/**
	 * 缩放图片（综合尺寸缩放和质量压缩）
	 * <pre>
	 * 缩放标识说明：
	 * 100 		width=100(对称拉伸)
	 * 100o100	width=100|height=100(智能拉伸)
	 * 100x100	width=100&height=100(强制拉伸)
	 * L10000	尺寸大于10000时，以10000为限额计算比例并压缩
	 * Q0.1		以0.1的比例压缩
	 * </pre>
	 * @param in	输入图片(流关闭)
	 * @param zoom	缩放标识：FileZoomTag
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	public static byte[] zooms(InputStream in, String zoom) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		zooms(in, out, zoom);
		return out.toByteArray();
	}
	
	/**
	 * 获取图片 - 根据字符串
	 * 
	 * @param str		图片文本
	 * @param width		图片宽
	 * @param height	图片高
	 * @param size		文本字号
	 * @param x			文本偏移x
	 * @param y			文本偏移y
	 * @param backColor	背景色，null表示透明
	 * 
	 * @return 图片：白底黑字
	 * 
	 * @author 延晓磊
	 * @throws IOException 
	 *
	 * @since 2020年5月24日
	 */
	public static BufferedImage getImgFromString(String str, int width, int height, int size, int x, int y, Color backColor)  {
		// 创建图片
		BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		// 绘制图片
		Graphics2D graphics = img.createGraphics();
		if (backColor != null) {
			// 填充背景
			graphics.setColor(Color.WHITE);
			graphics.fillRect(0, 0, img.getWidth(), img.getHeight());
		}else {
			// 透明背景
			img = graphics.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
			graphics.dispose();
			graphics = img.createGraphics();
		}
		// 填充文字
		graphics.setFont(new Font("宋体 ", Font.BOLD, size));
		graphics.setColor(Color.BLACK);
		graphics.drawString(str, x, y);
		// 释放画笔
		graphics.dispose();
		return img;
	}
	
	/**
	 * 获取图片 - 根据字符串平铺
	 * 
	 * @param str		图片文本
	 * @param width		图片宽
	 * @param height	图片高
	 * @param size		文本字号
	 * @param backColor	背景色，null表示透明
	 * 
	 * @return 图片：白底黑字
	 * 
	 * @author 延晓磊
	 * @throws IOException 
	 *
	 * @since 2020年5月24日
	 */
	public static BufferedImage getImgFromString(String str, int width, int height, int size, Color backColor)  {
		// 创建图片
		BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		// 绘制图片
		Graphics2D graphics = img.createGraphics();
		if (backColor != null) {
			// 填充背景
			graphics.setColor(Color.WHITE);
			graphics.fillRect(0, 0, img.getWidth(), img.getHeight());
		}else {
			// 透明背景
			img = graphics.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
			graphics.dispose();
			graphics = img.createGraphics();
		}
		// 填充文字
		graphics.setFont(new Font("宋体 ", Font.BOLD, size));
		graphics.setColor(Color.BLACK);
		int markWidth = str.length() * size;
		int markMargin = size * 2;
		for(int x = markMargin; x < width; x = x + markWidth + markMargin){
	         for(int y = markMargin; y < height; y = y + size + markMargin){
	        	// 平铺
	        	 graphics.drawString(str, x, y);
	         }
	     }
		// 释放画笔
		graphics.dispose();
		return img;
	}

	/**
	 * 获取水印图片
	 *
	 * @param mark 水印文字，如"DemonCat专用"
	 *
	 * @return 水印图片
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年5月24日
	 */
	public static Watermark getWatermark(String mark) {
		if (StringUtils.isBlank(mark)) {
			return null;
		}
		// 查询水印
		Watermark img = ImgUtil.WATERMARK_CACHE.get(mark);
		// 不存在初始化
		if (img == null) {
			// 创建水印图：透明、平铺；左上角，0.1透明
			img = new Watermark(Positions.TOP_LEFT, ImgUtil.getImgFromString(mark, 2000, 2000, 20, null), 0.1f);
			ImgUtil.WATERMARK_CACHE.put(mark, img);
		}
		return img;
	}

	/**
	 * 获取RGB值
	 *
	 * @param color image.getRGB()
	 * @return [R,G,B]
	 * 
	 * @author 延晓磊
	 * @since 2024年01月22日
	 */
	public static int[] getRgbs(int color){
		int red = (color & 0xff0000) >> 16;
		int green = (color & 0x00ff00) >> 8;
		int blue = (color & 0x0000ff);
		return new int[]{red, green, blue};
	}

	/**
	 * 抠图，转为PNG
	 *
	 * 例：ImgUtil.toPng(IoUtil.getInputStreamByFile("D:/1.jpg"),new FileOutputStream("D:/1.png"), 150);
	 *
	 * @param in        原图
	 * @param out       新图
	 * @param range     背景色区间：0-255
	 *
	 * @author 延晓磊
	 * @since 2024年01月22日
	 */
	public static void toPng(InputStream in, OutputStream out, int range){
		try {
			// 读取图片
			BufferedImage originalImage = ImageIO.read(in);
			BufferedImage image;
			if (isPng(originalImage)) {
				// 已经是PNG，直接输出
				image = originalImage;
			}else{
				// 新建图片
				image = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
				// 获取画笔
				Graphics2D graphics = (Graphics2D) image.getGraphics();
				// 绘制原图
				graphics.drawImage(new ImageIcon(originalImage).getImage(), 0, 0, null);
				// 遍历像素
				int rgb;
				int[] rgbs;
				int alpha;
				for (int y = image.getMinY(); y < image.getHeight(); y++) {
					for (int x = image.getMinX(); x < image.getWidth(); x++) {
						// 获取颜色
						rgb = image.getRGB(x, y);
						rgbs = getRgbs(image.getRGB(x, y));
						// 判断是否为背景
						if (rgbs[0] >= range && rgbs[1] >= range && rgbs[2] >= range){
							// 透明
							alpha = 0;
						}else{
							// 不透明
							alpha = 255;
						}
						// 设置透明色
						image.setRGB(x, y, (alpha << 24) | (rgb & 0x00ffffff));
					}
				}
				// 重绘图片
				graphics.drawImage(image, 0, 0, null);
			}
			// 输出图片
			ImgUtil.out(image, ImgConstant.Format.PNG, out);
		} catch (IOException e) {
			throw new IoRuntimeException("生成PNG图片失败", e);
		}finally {
			CloseUtil.close(in);
			CloseUtil.close(out);
		}
	}

	/**
	 * 抠图，转为PNG
	 *
	 * 例：ImgUtil.toPng(IoUtil.getInputStreamByFile("D:/1.jpg"),new FileOutputStream("D:/1.png"), 150);
	 *
	 * @param in        原图
	 * @param range     背景色区间：0-255
	 *
	 * @author 延晓磊
	 * @since 2024年01月22日
	 */
	public static byte[] toPng(InputStream in, int range){
		ByteArrayOutputStream out = IoUtil.getOutputStream();
		toPng(in, out, range);
		return out.toByteArray();
	}

	/**
	 * 判断是否为PNG（透明图片）
	 *
	 * @param image
	 * @return
	 * @author 延晓磊
	 * @since 2024年01月22日
	 */
	public static boolean isPng(BufferedImage image){
		int width = image.getWidth();
		int height = image.getHeight();
		int rgb;
		// 遍历像素
		for(int w=0; w<width; w++){
			for(int h=0; h<height; h++){
				rgb = image.getRGB(w,h);
				if(rgb >> 24==0){
					// 有透明像素
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 设置Thumbnails参数
	 * 
	 * @param of		Thumbnails		
	 * @param param		图片参数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月23日
	 */
	private static void setParam(Builder<?> of, ImgParam param) {
		// 缩放(zoom,width,height,scale必选其一，否则会报错size is not set)
		if (StringUtils.isNotBlank(param.getZoom())) {
			// 根据标识缩放
			String[] arr;
			if (param.getZoom().contains(FileZoomTag.WH)) {
				// 宽*高，对称缩放
				arr = StringUtils.split(param.getZoom(),FileZoomTag.WH);
				of.size(Integer.parseInt(arr[0]), Integer.parseInt(arr[1]));
			}else if (param.getZoom().contains(FileZoomTag.FORCE_WH)) {
				// 宽x高，强制拉伸
				arr = StringUtils.split(param.getZoom(),FileZoomTag.FORCE_WH);
				of.forceSize(Integer.parseInt(arr[0]), Integer.parseInt(arr[1]));
			}else {
				// 宽，对称缩放
				of.width(Integer.parseInt(param.getZoom()));
			}
		}else {
			// 根据宽高指定缩放
			if (!NumberUtil.isBlank(param.getWidth()) && !NumberUtil.isBlank(param.getHeight())) {
				// 宽x高，强制拉伸
				of.forceSize(param.getWidth(), param.getHeight());
			}else if (!NumberUtil.isBlank(param.getWidth())) {
				// 宽，对称缩放
				of.width(param.getWidth());
			}else if (!NumberUtil.isBlank(param.getHeight())) {
				// 高，对称缩放
				of.height(param.getHeight());
			}else {
				// 根据比例缩放
				if (!NumberUtil.isBlank(param.getScale())) {
					of.scale(param.getScale());
				}else {
					// 默认不缩放（确保至少有1个缩放参数）
					of.scale(1.0);
				}
			}
		}
		// 旋转
		if (!NumberUtil.isBlank(param.getRotate())) {
			of.rotate(param.getRotate());
		}
		// 裁剪
		if (param.getCrop() != null) {
			of.sourceRegion(param.getCrop());
		}
		// 水印
		if (param.getWatermark() != null) {
			of.watermark(param.getWatermark());
		}
		// 压缩
		if (!NumberUtil.isBlank(param.getQuality())) {
			of.outputQuality(param.getQuality());
		}
		// 格式转换
		if (StringUtils.isNotBlank(param.getFormat())) {
			of.outputFormat(param.getFormat());
		}
	}
	
}
